// P0145R2: Refining Expression Order for C++
// { dg-do run { target c++17 } }

extern "C" int printf (const char *, ...);
void sink(...) { }

int last = 0;
int f(int i)
{
  if (i < last)
    __builtin_abort ();
  last = i;
  return i;
}

int& g(int i)
{
  static int dummy;
  f(i);
  return dummy;
}

struct A
{
  int _i;
  A(int i): _i(f(i)) { }
  A& memfn(int i, int j) { f(j); return *this; }
  int operator<<(int i) { return 0; }
  A& operator=(const A&) { return *this; }
  A& operator+=(int i) { return *this; }
};

struct B
{
  int _i;
  B(): _i(0) {}
  B(int i): _i(f(i)) { }
};

int operator>>(A&, int i) { return 0; }

A a(0);
A* afn(int i)
{
  f(i);
  return &a;
}

A& aref(int i)
{
  f(i);
  return a;
}

B b;
B bval(int i)
{
  return B(i);
}

B& bref(int i)
{
  f(i);
  return b;
}

static int si;
int* ip (int i)
{
  f(i);
  return &si;
}

int& iref(int i)
{
  f(i);
  return si;
}

auto pmff(int i) {
  f(i);
  return &A::memfn;
}

template <class T> void f()
{
  // a.b
  A(1).memfn(f(2),3).memfn(f(4),5);
  aref(6).memfn(f(7),8);
  (aref(9).*pmff(10))(f(11),12);
  last = 0;

  // a->b
  afn(12)->memfn(f(13),14);

  // a->*b
  (afn(15)->*pmff(16))(f(17),18);
  last = 0;

  // a(b)
  // covered in eval-order1.C

  // b @= a
  aref(19)=A(18);
  iref(21)=f(20);
  aref(23)+=f(22);
  bref(25)=bval(24);
  (f(27), b) = bval(26);
  last = 0;

  int ar[4] = {};
  int i = 0;
  ar[i++] = i;
  if (ar[0] != 0)
    __builtin_abort();

  // a[b]
  afn(20)[f(21)-21].memfn(f(22),23);
  ip(24)[f(25)-25] = 0;
  last=0;

  // a << b
  aref(24) << f(25);
  iref(26) << f(27);
  last=0;

  // a >> b
  aref(26) >> f(27);
  iref(28) >> f(29);
}

void g()
{
  // a.b
  A(1).memfn(f(2),3).memfn(f(4),5);
  aref(6).memfn(f(7),8);
  (aref(9).*pmff(10))(f(11),12);
  last = 0;

  // a->b
  afn(12)->memfn(f(13),14);

  // a->*b
  (afn(15)->*pmff(16))(f(17),18);
  last = 0;

  // a(b)
  // covered in eval-order1.C

  // b @= a
  aref(19)=A(18);
  iref(21)=f(20);
  aref(23)+=f(22);
  bref(25)=bval(24);
  (f(27), b) = bval(26);
  last = 0;

  int ar[4] = {};
  int i = 0;
  ar[i++] = i;
  if (ar[0] != 0)
    __builtin_abort();

  // a[b]
  afn(20)[f(21)-21].memfn(f(22),23);
  ip(24)[f(25)-25] = 0;
  last=0;

  // a << b
  aref(24) << f(25);
  iref(26) << f(27);
  last=0;

  // a >> b
  aref(26) >> f(27);
  iref(28) >> f(29);
}

int main()
{
  g();
  last = 0;
  f<int>();
}
